using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using Cysharp.Threading.Tasks;
using TEngine;
using UnityEngine;
using UnityWebSocket;

namespace GameLogic
{
    public class SocketConfig
    {
         //public static string SocketURL { get; set; } = "wss://goproxy1.nbgame.cn:443"; //正式地址

        //public static string SocketURL { get; set; } = "wss://LobbyVan1.nbgame.cn:8443";  //线上测试
        public static string SocketURL { get; set; } = "ws://goproxytest1.nbgame.cn:81";  //内测
        
        //public static string SocketURL { get; set; } = "ws://192.168.2.47:81";

        public static int MAX_PING_HISTORY = 5;

        public static float DEFAULT_HEARTBEAT_INTERVAL = 3f;

        /// <summary>
        /// 心跳包无反应自动断连的次数，大于这个次数时，自动重连
        /// </summary>
        public static int AUTO_CLOSE_AND_RECONNECT_TIMES = 3;
    }

    /// <summary>
    /// Socket封装管理类，包括对UnityWebSocket的封装、心跳包的处理、网络质量的计算等
    /// SocketProxy持有SocketManager的实例，并在SocketManager中注册回调
    /// </summary>
    public sealed class SocketManager
    {
        //获取到消息后的回调,cmdid、剩余未读取二进制数据、包头
        public Action<uint, byte[], NewHeadReceive> OnGetSocketData;
        //Socket断开链接的回调，string参数为原因
        public Action<string> OnSocketClosed;
        //更新网络情况的委托
        public delegate void UpdatePingDelegate(float currentPing, float averagePing);
        //更新网络情况的回调
        public UpdatePingDelegate OnUpdateNetInfo;

        //WebSocket实例对象
        private WebSocket _socket;

        #region 网络质量相关字段
        // 默认5秒发送一次心跳
        private float _heartbeatInterval = SocketConfig.DEFAULT_HEARTBEAT_INTERVAL;
        //取消心跳异步操作
        private CancellationTokenSource _heartbeatCts;
        //最近一次发送心跳包的时间
        private DateTime _lastHeartbeatSentTime;
        //发送心跳包的次数，回包时会清空
        private uint _sendHeartBeatTimes = 0;
        //ping值的历史队列
        private readonly Queue<float> _pingHistory = new Queue<float>(SocketConfig.MAX_PING_HISTORY);
        //当前ping值
        private float _currentPing;
        //平均ping值
        private float _averagePing;
        //尝试的重连次数
        private int reconnectAttempts = 0;
        #endregion

        #region 对外方法
        public SocketManager()
        {
            // 重新创建WebSocket实例
            _socket = new WebSocket(SocketConfig.SocketURL);
            // 重新注册事件回调
            _socket.OnOpen += OnOpen;
            _socket.OnClose += OnClose;
            _socket.OnMessage += OnMessage;
            _socket.OnError += OnError;
        }

        //打开连接
        public void Open()
        {
            _socket.ConnectAsync();
        }

        // 发送消息
        public void Send(byte[] data)
        {
            _socket.SendAsync(data);                                              
        }

        // 关闭连接
        public void Close()
        {
            Log.Info("主动关闭连接");
            _socket.CloseAsync();
        }

        // 修改心跳配置方法
        public void SetHeartbeatInterval(float seconds)
        {
            _heartbeatInterval = Mathf.Clamp(seconds, 1f, 60f);
        }
        #endregion

        #region WebSocket回调
        private void OnOpen(object sender, OpenEventArgs e)
        {
            reconnectAttempts = 0;

            RestartHeartbeat();
        }

        private void OnMessage(object sender, MessageEventArgs e)
        {
            if (e.IsBinary)
            {
                OldHeadReceive oldHead = new OldHeadReceive();
                oldHead.Read(e.RawData);
                NewHeadReceive newHead = null;

                if (oldHead.cmdId == SocketDefinition.NEW_HEAD_IDENTIFY)  //即为新包
                {
                    newHead = new NewHeadReceive();
                    newHead.Read(e.RawData[oldHead.APILength..]);
                }

                uint cmdId = newHead?.cmdId ?? oldHead.cmdId;

                //收到任意包，都算没断连，清空心跳包发送次数计数；并开启心跳包
                _sendHeartBeatTimes = 0;
                //Socket开启后开启心跳
                RestartHeartbeat();

                //处理心跳包
                if ((API_ID_BASE)cmdId == API_ID_BASE.HEARTBEAT_RESPONSE)
                {
                    UpdatePing();
                }
                else
                {
                    int headLength = oldHead.APILength + newHead.APILength;
                    OnGetSocketData?.Invoke(cmdId, e.RawData[headLength..], newHead);
                }
            }
            else if (e.IsText)
            {
                Log.Warning(string.Format("收到Text数据，OnMessage Receive: {0}", e.Data));
            }
        }

        private void OnClose(object sender, CloseEventArgs e)
        {
            Log.Info("Closed: StatusCode: {0}, Reason: {1}", e.StatusCode, e.Reason);
            // 取消心跳
            ClearHeartbeat();
            //触发关闭回调
            OnSocketClosed?.Invoke(e.Reason);

            ReOpen();
        }

        private async void ReOpen()
        {
            Log.Info($"尝试重连");

            await UniTask.Delay(1000);

            Open();
            Log.Info($"尝试第 {reconnectAttempts + 1} 次重连...");
            reconnectAttempts++;
        }

        private void OnError(object sender, ErrorEventArgs e)
        {
            Log.Error(string.Format("Error: {0}", e.Message));
        }
        #endregion

        #region 心跳相关

        private void UpdatePing()
        {
            // 计算延迟
            float newPing = (float)(DateTime.Now - _lastHeartbeatSentTime).TotalMilliseconds;

            // 更新历史Ping记录
            if (_pingHistory.Count >= SocketConfig.MAX_PING_HISTORY)
            {
                _pingHistory.Dequeue();
            }
            _pingHistory.Enqueue(newPing);

            // 计算当前Ping
            _currentPing = newPing;
            // 计算平均Ping
            _averagePing = _pingHistory.Average();

            //派发更新ping回调，具体表现交由外部处理
            OnUpdateNetInfo?.Invoke(_currentPing, _averagePing);
        }

        private async UniTaskVoid StartHeartbeatAsync(CancellationToken cancellationToken)
        {
            try
            {
                while (!cancellationToken.IsCancellationRequested)
                {
                    await UniTask.Delay(
                        TimeSpan.FromSeconds(_heartbeatInterval),
                        DelayType.Realtime, // 使用实时时间
                        PlayerLoopTiming.Update,
                        cancellationToken
                    );

                    SendHeartbeat();
                }
            }
            catch (OperationCanceledException)
            {
                // 正常取消，无需处理
            }
            catch (Exception e)
            {
                Log.Error($"Heartbeat task error: {e}");
            }
        }

        private void SendHeartbeat()
        {
            //记录发送时时间
            _lastHeartbeatSentTime = DateTime.Now;
            _sendHeartBeatTimes++;

            if(_sendHeartBeatTimes > SocketConfig.AUTO_CLOSE_AND_RECONNECT_TIMES)
            {
                //主动断线，等待OnClose事件
                Close();
                ClearHeartbeat();
                return;
            }
            Send(SendPack.SerializePacket(new HeartbeatPacket()));
        }

        private void RestartHeartbeat()
        {
            ClearHeartbeat();

            _heartbeatCts = new CancellationTokenSource();
            StartHeartbeatAsync(_heartbeatCts.Token).Forget();
        }

        private void ClearHeartbeat()
        {
            // 取消现有心跳任务
            _heartbeatCts?.Cancel();
            _heartbeatCts?.Dispose();
            _heartbeatCts = null;

            _pingHistory.Clear();
            _sendHeartBeatTimes = 0;
        }
        #endregion
    }
}